//
// OpenTemplate
// Copyright (c) 2006
// by OpenArrow Software ( http://www.openarrow.com )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
// to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions 
// of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.
//

using System;
using System.Collections.Generic;
using System.Text;
using OpenArrow.Templating.Parsing;
using System.CodeDom;
using OpenArrow.Templating.Properties;

namespace OpenArrow.Templating.Directives
{
    /// <summary>
    /// Defines member access identifiers
    /// </summary>
    public enum MemberAccess
    {
        /// <summary>
        /// The member is private
        /// </summary>
        Private,

        /// <summary>
        /// The member is public
        /// </summary>
        Public,

        /// <summary>
        /// The member is protected
        /// </summary>
        Protected,

        /// <summary>
        /// The member is internal
        /// </summary>
        Internal
    }

    /// <summary>
    /// Directive that defines a property and associated field in the class generated for the template
    /// </summary>
    [Directive("Property")]
    public class PropertyDirective : DirectiveBase
    {
        #region Private Fields

        private string name;
        private string fieldName;
        private string type;
        private MemberAccess access = MemberAccess.Public;

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the name of the property
        /// </summary>
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        /// <summary>
        /// Gets or sets the name of the field associated with the property
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.Char.ToLower(System.Char)")]
        public string FieldName
        {
            get
            {
                if (String.IsNullOrEmpty(fieldName))
                    return String.Concat("_", this.Name.Replace(this.Name[0], Char.ToLower(this.Name[0])));
                return fieldName;
            }
            set { fieldName = value; }
        }

        /// <summary>
        /// Gets or sets the type of the property
        /// </summary>
        public string Type
        {
            get { return type; }
            set { type = value; }
        }

        /// <summary>
        /// Gets or sets a value from the <see cref="MemberAccess"/> enumeration representing the access level of the property
        /// </summary>
        public MemberAccess Access
        {
            get { return access; }
            set { access = value; }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Executes the Directive
        /// </summary>
        /// <param name="context">Context information for the execution of the directive</param>
        /// <exception cref="OpenArrow.Templating.ActionProblemsException">A large number of errors occurred in the operation and it had to be aborted</exception>
        public override void Execute(DirectiveContext context)
        {
            base.Execute(context);
            using (context.Reporter.StartTask(String.Format(Resources.Task_ExecutingDirective, "Property")))
            {
                if (String.IsNullOrEmpty(Name))
                {
                    context.Problems.Add(Problem.Error_NoPropertyNameSpecified());
                    return;
                }
                if (String.IsNullOrEmpty(Type))
                {
                    context.Problems.Add(Problem.Error_NoPropertyTypeSpecified());
                    return;
                }

                CodeMemberProperty property = new CodeMemberProperty();
                CodeMemberField field = new CodeMemberField();
                CodeTypeReference typeReference = new CodeTypeReference(this.Type);
                field.Name = this.FieldName;
                field.Attributes = MemberAttributes.Private;
                field.Type = typeReference;
                property.Name = this.Name;
                property.Type = typeReference;
                MemberAttributes attributes;
                switch (Access)
                {
                    case MemberAccess.Internal:
                        attributes = MemberAttributes.Assembly;
                        break;
                    case MemberAccess.Private:
                        attributes = MemberAttributes.Private;
                        break;
                    case MemberAccess.Protected:
                        attributes = MemberAttributes.Family;
                        break;
                    default:
                        attributes = MemberAttributes.Public;
                        break;
                }
                property.Attributes = attributes;

                property.GetStatements.Add(new CodeMethodReturnStatement(
                    new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name)));
                property.SetStatements.Add(new CodeAssignStatement(
                    new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name),
                    new CodePropertySetValueReferenceExpression()));

                context.TemplateType.Members.Add(field);
                context.TemplateType.Members.Add(property);
            }
        }

        #endregion
    }
}
